package com.loe.mvp.ext_java

import android.app.Activity
import android.graphics.Color
import android.os.Handler
import android.os.Looper
import android.text.Html
import android.text.Spanned
import android.util.Log
import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment
import java.lang.ref.WeakReference
import java.math.RoundingMode
import java.text.DecimalFormat
import java.util.*

inline val String?.int: Int
    get() = try
    {
        this?.toDouble()?.toInt() ?: 0
    } catch (e: Exception)
    {
        0
    }

inline val String?.long: Long
    get() = try
    {
        this?.toDouble()?.toLong() ?: 0L
    } catch (e: Exception)
    {
        0L
    }

inline val String?.boolean: Boolean
    get() = try
    {
        this == "1" || this?.toBoolean() ?: false
    } catch (e: Exception)
    {
        false
    }

inline val String?.double: Double
    get() = try
    {
        this?.toDouble() ?: 0.0
    } catch (e: Exception)
    {
        0.0
    }

inline val String?.float: Float
    get() = try
    {
        this?.toFloat() ?: 0f
    } catch (e: Exception)
    {
        0f
    }

inline val Boolean?.int: Int
    get() = if (this == true) 1 else 0

inline val String?.not0: String
    get() = if (this == null || this.double == 0.0) "" else this

inline val String?.string: String
    get() = if (this == null || this == "null") "" else this

inline val String?.stringNull: String?
    get() = if (this == null || isEmpty()) null else this

inline val String?.doubleLimit: String
    get() = DecimalFormat("0.##").format(this.double)

inline val Int?.string: String
    get() = this?.toString() ?: ""

inline val Double?.string: String
    get()
    {
        if (this != null && this - this.toLong() == 0.0) return this.toLong().toString()
        return this?.toString() ?: ""
    }

inline val Double?.limitString: String
    get() = if (this == null || this == 0.0) "0" else ((this * 100).toLong() / 100.0).string

inline val Double?.stringOrEmpty: String
    get()
    {
        if (this == null || this == 0.0) return ""
        if (this - this.toLong() == 0.0) return this.toLong().toString()
        return this.toString()
    }

inline val Float?.string: String
    get()
    {
        if (this != null && this - this.toLong() == 0f) return this.toLong().toString()
        return this?.toString() ?: ""
    }

inline val Long?.string: String
    get() = this?.toString() ?: ""

inline val Boolean?.string: String
    get() = this?.toString() ?: ""

inline val Double?.moneyLong: Long
    get()
    {
        val l = (this ?: 0.0) * 100
        val d = if (l < 0) l - 0.00001 else l + 0.00001
        return d.toLong()
    }

inline val Long?.percentMoney: String
    get()
    {
        var d = (this ?: 0L) / 100.0
        d = if (d < 0) d - 0.0000001 else d + 0.0000001
        return DecimalFormat("0.00").format(d)
    }

inline val Double?.money: String
    get()
    {
        var d = this ?: 0.0
        d = if (d < 0) d - 0.0000001 else d + 0.0000001
        return DecimalFormat("0.00").apply { roundingMode = RoundingMode.DOWN }.format(d)
    }

inline val String?.money: String
    get()
    {
        var d = double
        d = if (d < 0) d - 0.0000001 else d + 0.0000001
        return DecimalFormat("0.00").apply { roundingMode = RoundingMode.DOWN }.format(d)
    }

inline val String.color: Int
    get()
    {
        return try
        {
            Color.parseColor(this)
        } catch (e: Exception)
        {
            0
        }
    }

inline fun safe(run: () -> Unit)
{
    try
    {
        run()
    } catch (e: Exception)
    {
        Log.e("AndroidRuntime", "" + e)
    }
}

inline fun CharSequence?.ifNotEmpty(run: (c: CharSequence) -> Unit)
{
    if (this != null && this.isNotEmpty())
    {
        run(this)
    }
}

/** 延时 */
fun delay(delay: Long? = null, run: () -> Unit): DelayTask
{
    return object : DelayTask(delay)
    {
        override fun run()
        {
            run()
        }
    }.start()
}

/** 延时（防止泄漏） */
fun <T> T?.weakDelay(delay: Long? = null, wakeRun: T.() -> Unit): DelayTask
{
    val weak = WeakReference(this)
    return object : DelayTask(delay)
    {
        override fun run()
        {
            weak.get()?.wakeRun()
        }
    }.start()
}

/** 安全延时 */
fun delaySafe(delay: Long? = null, run: () -> Unit): DelayTask
{
    return object : DelayTask(delay)
    {
        override fun run()
        {
            safe { run() }
        }
    }.start()
}

/** 安全延时（防止泄漏） */
fun <T> T?.weakDelaySafe(delay: Long? = null, wakeRun: T.() -> Unit): DelayTask
{
    val weak = WeakReference(this)
    return object : DelayTask(delay)
    {
        override fun run()
        {
            safe { weak.get()?.wakeRun() }
        }
    }.start()
}

/** 定时 */
fun timer(delay: Long = 0, period: Long = 1000, times: Long = java.lang.Long.MAX_VALUE, run: (lastTimes: Long) -> Unit): AsyncTimer
{
    return object : AsyncTimer(delay, period, times)
    {
        override fun run(lastTimes: Long)
        {
            run(lastTimes)
        }
    }.start()
}

/** 定时（防止泄漏） */
fun <T> T?.weakTimer(delay: Long = 0, period: Long = 1000, times: Long = java.lang.Long.MAX_VALUE, wakeRun: T.() -> Unit): AsyncTimer
{
    val weak = WeakReference(this)
    return object : AsyncTimer(delay, period, times)
    {
        override fun run(lastTimes: Long)
        {
            val o = weak.get()
            if (o == null || o is Activity && o.isFinishing || o is Fragment && o.isDetached)
            {
                stop()
            } else
            {
                o.wakeRun()
            }
        }
    }.start()
}

inline val Int.to99: String get() = if (this > 99) "99+" else toString()

inline val CharSequence.b: String get() = "<b>$this</b>"

inline val CharSequence.i: String get() = "<i>$this</i>"

inline val CharSequence.u: String get() = "<u>$this</u>"

fun CharSequence.c(c: String): String
{
    return "<font color='$c'>$this</font>"
}

fun CharSequence.s(s: Int): String
{
    return "<font size='$s'>$this</font>"
}

inline val CharSequence.red: String get() = c("#ff0000")

inline val CharSequence.blue: String get() = c("#5084fe")

inline val Int.toStringColor: String
    get()
    {
        val red = (this shr 16) and 0xFF
        val green = (this shr 8) and 0xFF
        val blue = this and 0xFF
        return "#%02X%02X%02X".format(red, green, blue)
    }

inline val String.html: Spanned get() = HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_LEGACY)

inline val String.phone: String
    get() = try
    {
        this.substring(0, 3) + "****" + this.substring(7)
    } catch (e: Exception)
    {
        this
    }

inline val String.card: String
    get() = try
    {
        this.substring(0, 6) + "********" + this.substring(14)
    } catch (e: Exception)
    {
        this
    }

fun String.hide(start: Int, end: Int = Int.MAX_VALUE): String
{
    val end = if (end <= 0) length + end else end
    return try
    {
        if (length <= end)
        {
            if (length <= start)
            {
                this
            } else
            {
                this.substring(0, start) + "*".repeat(length - start)
            }
        } else
        {
            this.substring(0, start) + "*".repeat(end - start) + this.substring(end)
        }
    } catch (e: Exception)
    {
        this
    }
}

fun String.getSpan(start: String, end: String): String
{
    val regex = Regex("$start(.*)$end")

    // 查找匹配项
    val matchResult = regex.find(this)

    return matchResult?.groupValues?.get(1) ?: ""
}

fun Any?.putFieldsTo(o: Any)
{
    safe()
    {
        o.javaClass.declaredFields.forEach()
        { field ->
            field.isAccessible = true
            val value = field.get(this)
            if (value != null) field.set(o, value)
        }
    }
}

fun Any?.loadFieldsBy(o: Any)
{
    safe()
    {
        o.javaClass.declaredFields.forEach()
        { field ->
            field.isAccessible = true
            val value = field.get(o)
            if (value != null) field.set(this, value)
        }
    }
}

fun Any?.putAllFieldsTo(o: Any)
{
    safe()
    {
        var cls: Class<*>? = o.javaClass
        while (cls != null)
        {
            cls.declaredFields.forEach()
            { field ->
                field.isAccessible = true
                val value = field.get(this)
                if (value != null) field.set(o, value)
            }
            cls = cls.superclass
        }
    }
}

fun Any?.loadAllFieldsBy(o: Any)
{
    safe()
    {
        var cls: Class<*>? = o.javaClass
        while (cls != null)
        {
            cls.declaredFields.forEach()
            { field ->
                field.isAccessible = true
                val value = field.get(o)
                if (value != null) field.set(this, value)
            }
            cls = cls.superclass
        }
    }
}

val String.packName: String
    get()
    {
        val ps = split(".")
        return if (ps.size > 3)
        {
            ps[0] + "." + ps[1] + "." + ps[2]
        } else
        {
            substring(0, lastIndexOf("."))
        }
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

val mainHandler by lazy { Handler(Looper.getMainLooper()) }

abstract class DelayTask(private val delay: Long? = null) : Runnable
{
    fun start(): DelayTask
    {
        if (delay == null)
        {
            mainHandler.post(this)
        } else
        {
            mainHandler.postDelayed(this, delay)
        }

        return this
    }

    fun stop()
    {
        mainHandler.removeCallbacks(this)
    }
}

abstract class AsyncTimer(
    private val delay: Long = 0,
    private val period: Long = 1000,
    var lastTimes: Long = Long.MAX_VALUE
) : Timer()
{
    private var task: TimerTask? = null

    var isPause = false
        private set

    open fun start(): AsyncTimer
    {
        try
        {
            isPause = false
            task = object : TimerTask()
            {
                override fun run()
                {
                    if (lastTimes > 0)
                    {
                        if (lastTimes != Long.MAX_VALUE) lastTimes--
                        val times = lastTimes
                        mainHandler.post()
                        {
                            run(times)
                        }
                    } else
                    {
                        task = null
                    }
                }
            }
            schedule(task, delay, period)
        } catch (e: Exception)
        {
        }
        return this
    }

    open fun resume(): AsyncTimer
    {
        if (isPause)
        {
            start()
        }
        return this
    }

    open fun pause(): AsyncTimer
    {
        if (task != null)
        {
            isPause = true
            task?.cancel()
        }
        return this
    }

    open fun stop(): AsyncTimer
    {
        pause()
        cancel()
        return this
    }

    abstract fun run(lastTimes: Long = 0)
}